<?php
namespace zake\app\wChat;


use zake\helper\Console;

class Package
{

    // 首次连接成功，可以基于sessionID和timestamp成功一个public key传给client
    // 服务端 encode(session id , body) -> hash code

    //    255    +    255    +    255    +    255    +
    // ---- ---- + ---- ---- + ---- ---- + ---- ---- +
    //  用户消息M +     /     +     /     +  系统消息S | 1 byte     0-1
    // ---- ---- + ---- ---- + ---- ---- + ---- ---- +
    //                   hash code                   | 32 byte    1-33
    // ---- ---- + ---- ---- + ---- ---- + ---- ---- +
    //                version code                   | 1 byte     33-34
    // ---- ---- + ---- ---- + ---- ---- + ---- ---- +
    //                   timestamp                   | 1 byte     34-35
    // ---- ---- + ---- ---- + ---- ---- + ---- ---- +
    //   body type: [1-raw. 2-json. 3-xml. 4-html]   | 1 byte     35-36
    // ---- ---- + ---- ---- + ---- ---- + ---- ---- +
    //                      body                     | x byte     36-
    // ---- ---- + ---- ---- + ---- ---- + ---- ---- |


    // 版本号
    const VERSION_CODE = 0X00020201;

    // 系统消息 0X00000001 - 0X000000FF
    const TYPE_S_LOGIN_FAIL       = 0X00000001;   // 登陆失败
    const TYPE_S_LOGIN_SUCCESS    = 0X00000002;   // 登陆成功
    const TYPE_S_USER_ON          = 0X00000011;   // 用户上线
    const TYPE_S_USER_OFF         = 0X00000012;   // 用户下线
    const TYPE_S_FRI_ON           = 0X00000013;   // 好友上线
    const TYPE_S_FRI_OFF          = 0X00000014;   // 好友下线
    const TYPE_S_EXT              = 0X000000FF;   // 扩展类型（从body中解析）

    // 用户息消 0X01000000 - 0XFF000000
    const TYPE_M_TEXT     = 0X01000000;  // 文本
    const TYPE_M_IMAGE    = 0X02000000;  // 图片
    const TYPE_M_EXT      = 0XFF000000;  // 扩展类型（从body中解析）


    // body格式
    const BODY_TYPE_RAW     = 0X00000001;
    const BODY_TYPE_JSON    = 0X00000002;
    const BODY_TYPE_XML     = 0X00000003;
    const BODY_TYPE_HTML    = 0X00000004;


    private $type           = 0;
    private $connectKey     = 0;
    private $body           = "";
    private $bodyType       = 2;


    /**
     * 构造器
     * @param $type
     * @param $sessionId
     * @param string|array|int $body
     * @return static
     *
     * @author liu.bin 2020-05-12 13:45
     */
    public static function create($type,$connectKey,$body)
    {
        $self = new static();
        $self->type         = $type;
        $self->connectKey   = $connectKey;
        $self->body         = $body;
        return $self;
    }



    /**
     * 打包
     * @return string
     *
     * @author liu.bin 2020-05-07 14:16
     */
    public function encode()
    {
        $body       = $this->encodeBody($this->body);
        $hashCode   = md5(md5($body).$this->connectKey);
        $buffer     = pack('N',$this->type) . $hashCode . pack('N',self::VERSION_CODE) . pack('N',time()).pack('N',$this->bodyType).$body;
        return $buffer;
    }


    /**
     * 解包
     *
     * @author liu.bin 2020-05-12 17:26
     */
    public function decode($buffer)
    {
        $type       = substr($buffer,0,4);
        $hashCode2  = substr($buffer,4,32);
        $version2   = substr($buffer,36,4);
        $time2      = substr($buffer,40,4);


        Console::warnning($buffer);

        Console::warnning("------type-------");
        $unpack = unpack('Ntype', $type);
        $type = $unpack['type']??0;
        Console::warnning($type);
        Console::warnning($this->type);

        Console::warnning("------hash code-------");
        //Console::warnning("code:[{$hashCode}]. len:[".strlen($hashCode)."]");
        Console::warnning("code:[{$hashCode2}]. len:[".strlen($hashCode2)."]");


        Console::warnning("------version code-------");
        $unpack     = unpack('Nversion', $version2);
        $version2   = $unpack['version']??0;
        Console::warnning($version2);
        Console::warnning(self::VERSION_CODE);


        Console::warnning("------time-------");
        $unpack     = unpack('Ntime', $time2);
        $time2      = $unpack['time']??0;
        Console::warnning($time2);
        Console::warnning(time());

    }



    /**
     * body 编码
     * @param array|string|int $body
     * @return string
     * @author liu.bin 2020-05-12 13:32
     */
    private function encodeBody($body)
    {
        $bodyStr = "";
        switch ($this->bodyType){
            case 1:
                $bodyStr = is_scalar($body) ? $body : "";
                break;
            case 2:
                $bodyStr = json_encode($body,JSON_UNESCAPED_SLASHES | JSON_UNESCAPED_UNICODE);
                break;
            case 3:
                // todo xml
                break;
            case 4:
                $bodyStr = is_scalar($body) ? $body : "";
                break;
        }
        return $bodyStr;
    }


}


